nuxt

记录一下初学nuxt.js的知识点📕

介绍

官网:https://zh.nuxtjs.org/guide

特性:

  • 基于 Vue.js
  • 自动代码分层
  • 服务端渲染
  • 强大的路由功能,支持异步数据
  • 静态文件服务
  • ES6/ES7 语法支持
  • 打包和压缩 JS 和 CSS
  • HTML头部标签管理
  • 本地开发支持热加载
  • 集成ESLint
  • 支持各种样式预处理器: SASS、LESS、 Stylus等等

安装

1
vue init nuxt-community/starter-template <project-name>

ps: 如果没有安装 vue-cli ,需先通过 npm i -g vue-cli 来安装

安装依赖包

1
2
cd <project-name>
npm install

启动项目

1
npm run dev

应用运行在:http://localhost:3000 ,后面讲如何修改默认端口。

目录结构说明

1
2
3
4
5
6
7
8
9
10
├── assets //用于组织未编译的静态资源如 LESS、SASS 或 JavaScript。
├── components //用于组织应用的 Vue.js 组件。
├── layputs //用于组织应用的布局组件。
├── middleware //用于存放应用的中间件。
├── pages //用于组织应用的路由及视图。Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置。
├── plugins //用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。
├── static //用于存放应用的静态文件。
├── store //用于组织应用的 Vuex 状态树 文件。
├── nuxt.config.js //用于组织Nuxt.js 应用的个性化配置,以便覆盖默认配置。
└── package.json //用于描述应用的依赖关系和对外暴露的脚本接口。

路由

Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置。

假设 pages 的目录结构如下:

1
2
3
4
5
pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue

那么,Nuxt.js 自动生成的路由配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'user',
path: '/user',
component: 'pages/user/index.vue'
},
{
name: 'user-one',
path: '/user/one',
component: 'pages/user/one.vue'
}
]
}

视图

模板

你可以定制化 Nuxt.js 默认的应用模板。

定制化默认的 html 模板,只需要在应用根目录下创建一个 app.html 的文件。

默认模板为:

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>

布局

  • 默认布局
1
2
3
<template>
<nuxt/>
</template>
  • 个性化布局

layouts 根目录下的所有文件都属于个性化布局文件,可以在页面组件中利用 layout 属性来引用。

请确保在布局文件里面增加 <nuxt/> 组件用于显示页面非布局内容。

for example: layouts/default.vue

1
2
3
4
5
6
<template>
<div>
<div>这里是导航内容</div>
<nuxt/>
</div>
</template>

在 pages/index.vue 里,可以指定页面组件使用该布局

1
2
3
4
5
<script>
export default {
layout: 'blog'
}
</script>

页面

页面组件实际上是 Vue 组件,只不过 Nuxt.js 为这些组件添加了一些特殊的配置项(对应 Nuxt.js 提供的功能特性)以便你能快速开发通用应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<h1 class="red">Hello {{ name }}!</h1>
</template>

<script>
export default {
asyncData (context) {
// called every time before loading the component
return { name: 'World' }
},
fetch () {
// The fetch method is used to fill the store before rendering the page
},
head () {
// Set Meta Tags for this Page
},
// and more functionality to discover
...
}
</script>

<style>
.red {
color: red;
}
</style>

插件

使用 element UI

首先增加文件 plugins/element-ui.js

1
2
3
4
import Vue from 'vue'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(Element, { size: 'small' })

在 nuxt.config.js 中配置

1
2
3
plugins: [
'~plugins/element-ui'
]

只在浏览器里使用的插件

nuxt.config.js

1
2
3
4
5
module.exports = {
plugins: [
{ src: '~plugins/element-ui', ssr: false }
]
}

异步数据

asyncData

在渲染组件之前异步获取数据。

asyncData方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用 asyncData方法来获取数据并返回给当前组件。

1
2
3
4
5
6
7
8
export default {
async asyncData (context) {
let data = await axios.get('http://www.xxx);
return {
title: data.title
}
}
}

fetch

用于在渲染页面前填充应用的状态树(store)数据, 与 asyncData 方法类似,不同的是它不会设置组件的数据。

如果页面组件设置了 fetch 方法,它会在组件每次加载前被调用(在服务端或切换至目标路由之前)。

fetch 方法的第一个参数是页面组件的上下文对象 context,我们可以用 fetch 方法来获取数据填充应用的状态树。为了让获取过程可以异步,你需要返回一个 Promise,Nuxt.js 会等这个 promise 完成后再渲染组件。

1
2
3
4
5
6
export default {
async fetch({store, params}) {
let data = await axios.get('http://www.xxx')
store.commit('setValues', data);
}
}

配置项

nuxt.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
module.exports = {
head: {
title: 'douban',
meta: [
{
charset: 'utf-8'
},{
name: 'viewport',
content: 'width=device-width,initial-scale=1'
},{
hid: 'description',
name: 'description',
content: 'Meta description'
}
],
//ali iconfont
link: [
{
'rel': 'stylesheet',
'href': '//at.alicdn.com/t/font_562725_orktergz2etawcdi.css'
}
]
},
css: [
{
src: '~assets/css/reset.scss',
lang: 'scss'
},{
src: '~assets/css/style.scss',
lang: 'scss'
}
],
loading: {
color: '#00b600',
height: '2px',
failedColor: 'red'
},
plugins: [
'~plugins/filters',
'~plugins/element-ui'
],
build: {
vendor: ['axios']
},
router: {
linkActiveClass: 'active-link',
}
}

常见问题

修改端口号

package.json

1
2
3
4
5
6
7
8
9
"config": {
"nuxt": {
"host": "0.0.0.0",
"port": "4000"
}
},
"scripts": {
"dev": "nuxt"
}

使用外部资源

在 nuxt.config.js 中配置你想引用的资源文件:

1
2
3
4
5
6
7
8
9
10
module.exports = {
head: {
link: [
{
'rel': 'stylesheet',
'href': '//at.alicdn.com/t/font_562725_orktergz2etawcdi.css'
}
]
}
}

局部配置
可在 pages 目录内的 .vue 文件中引用外部资源,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<h1>使用 jQuery 和 Roboto 字体的关于页</h1>
</template>

<script>
export default {
head: {
script: [
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js' }
]
}
}
</script>

meta 标签重复

为了避免子组件中的meta标签不能正确覆盖父组件中相同的标签而产生重复的现象,建议利用 hid 键为meta标签配一个唯一的标识编号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
head: {
title: 'starter',
meta: [
{
charset: 'utf-8'
},
{
name: 'viewport',
content: 'width=device-width,
initial-scale=1'
},
{
hid: 'description',
name: 'description',
content: 'This is the generic description.'
}
],
}

Comment and share

搭建博客

记录下搭建博客的方法📕~

文档

安装

  • Node.js
  • Git
1
2
//安装Hexo
npm install -g hexo-cli

建站

1
2
3
hexo init <folder>
cd <folder>
npm install

新建完成之后指定文件夹目录如下

1
2
3
4
5
6
7
├── _config.yml //在此文件中配置git仓库,下面会详细介绍
├── package.json
├── scaffolds
├── source
| ├── _drafts
| └── _posts
└── themes

到此站已经建好了,下面去Github上建立自己的仓库吧!🎉🎉🎉🎉

建立仓库

img
img

ps:Repository name必须是 xxx.github.io (xxx是你的github username)

安装必备插件

1
2
npm install hexo-deployer-git
npm install --save hexo-render-pug hexo-generator-archive hexo-generator-tag hexo-generator-index hexo-generator-category

配置仓库

1
2
3
4
5
6
7
8
9
10
11
//_config.yml
deploy:
type: git
repo: 仓库git地址
branch: master

archive_generator:
per_page: 0
yearly: false
monthly: false
daily: false

写博客

1
2
3
4
5
6
//<title>是文章标题
hexo new <title>
//此命令结束后,会在 souce/_posts 下出现对应的文件

//上传静态文件
hexo g -d //hexo generator deployer

完成

浏览https://xxx.githu.io ✌✌✌

安装主题

主题模板

  • 安装主题之前先把默认主题清空

    1
    hexo clean
  • tranquilpeak为例。

img

1
2
3
4
5
6
7
1、Run git clone https://github.com/LouisBarranqueiro/hexo-theme-tranquilpeak.git
2、Rename the folder in tranquilpeak and place it in themes folder of your Hexo blog
3、Modify the theme in _config.yml by changing theme variable to tranquilpeak
4、Complete theme/tranquilpeak/_config.yml with your information by following directives in comments
5、Go in theme/tranquilpeak folder with cd themes/tranquilpeak
6、Install requirements
7、Run npm install to install NPM dependencies

Comment and share

vue总结

最近刚刚写好了网易云音乐(wymusic)的实例,那就总结总结吧~~~~

先感谢感谢前人的努力吧

因为Binaryify大神对接口的不对更新才有了这篇文章,感谢感谢!

公共引用

main.js(以element ui的使用为例)

1
2
3
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(Element)

关于分类

将view文件统一放在 src/pages/ 下面

将component文件统一放在 src/components/ 下面

vue-router

router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mport Vue from 'vue'
import Router from 'vue-router'
import Recommend from '@/pages/Recommend'
import LeaderBoards from '@/pages/LeaderBoards'
Vue.use(Router)

export default new Router({
routes: [
{
path:'',
redirect: '/recommend'
},
{
path: '/recommend',
name: 'recommend',
component: Recommend
}
]
})

src/app.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div id="app">
<router-view :key="key" />
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {}
},
methods: {
},
computed:{
key() {
return this.$route.name !== undefined? this.$route.name + +new Date(): this.$route + +new Date()
}
}
}
</script>

ps:我创建和编辑的页面使用的是同一个component,默认情况下当这两个页面切换时并不会触发vue的created或者mounted钩子,官方说你可以通过watch $route的变化来做处理,但其实说真的还是蛮麻烦的。后来发现其实可以简单的在 router-view上加上一个唯一的key,来保证路由切换时都会重新渲染触发钩子了。这样简单的多了。

组件引用

src/pages/Recommend.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div id="Recommend">
<BannerComponent></BannerComponent>
</div>
</template>
<script>
import BannerComponent from '@/components/BannerComponent'
export default {
name:"Recommend",
components:{
BannerComponent,
AlbumComponent,
SingersComponent,
MVComponent
},
data () {
return {};
}
}
</script>

组件之间通信

父子组件之间(props)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//在父组件中注册子组件
//父组件(Recommend.vue):
<template>
<BannerComponent :toBanner="bannerJson.banners" title="recommend-banner"></BannerComponent>
</template>

....

//子组件(BannerComponent.vue):
<template>
<div id="BannerComponent" v-if="title === 'recommend-banner'">
<swiper :options="swiperOption">
<swiper-slide v-for="(item,index) in toBanner" :key="index">
<img :src="item.pic | filterImg">
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
<div class="swiper-button-prev" slot="button-prev"></div>
<div class="swiper-button-next" slot="button-next"></div>
</swiper>
</div>
</template>
<script>
export default{
name: 'BannerComponent',
props:['toBanner','title'],
data () {
return {}
}
}
</script>

ps: (:toBanner)代表值是动态获取的;(title)代表值是静态的

子父组件传值(emit)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//子组件:
<template>
<button @click="sendToParent">向父组件传值</buttoon>
</template>
<script>
export default {
methods: {
sendToParent() {
this.$emit("listenToChild","this message is from child");
}
}
}
</script>

.....

//父组件:
<template>
<child listenToChild="showMsgFromChild"></child>
</template>
<script>
export default{
data(){
return {};
},
methods: {
showMsgFromChild(data){
console.log(data); //this message is from child
}
}
}
</script>

vuex

封装axios

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//src/config.js
import Vue from 'vue'
import axios from 'axios'
import qs from 'qs'
Vue.prototype.$http = axios // 这样设置就可以在组件内用 this.$http 使用axios了
axios.defaults.baseURL = 'http://localhost:3000'

export var axiosRq = async(type = 'POST', url = '', data = {}) => {
let result
type = type.toUpperCase()
if (type === 'GET') {
await axios.get(url, { params: data })
.then(res => {
result = res.data
})
} else if (type === 'POST') {
await axios.post(url, qs.stringify(data))
.then(res => {
result = res.data
})
}
return result
}

使用

  • 在src先新建store文件夹
  • store中目录包含 index.js 和 modules文件夹
  • 在src/main.js中引用store
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//src/store/index.js
import Vue from "vue"
import Vuex from 'vuex'
import banner from './modules/banner'
Vue.use(Vuex)

export default new Vuex.Store({
modules: {
banner
}
})


src/store/modules/banner.js

import { axiosRq } from "../../config"
export default {
state: {
banner:[]
},
mutations: {
GETBANNER (state, res) {
state.banner = res;
}
},
actions: {
async getBannerData ({commit}) {
let res = await axiosRq('GET', 'banner')
if(res) {
commit('GETBANNER',res);
}
}
}
}

在view中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//Recommend.vue
<template>
{{bannerJson}}
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
data () {
return {}
},
methods: {
...mapAction([
'getBannerData' //相当于this.$store.dispatch('getBannerData')
])
},
computed: {
...mapState({
'bannerJson': state = > state.banner.banner, //第一个banner是export的banner对象,第二个banner是banner.js state值
})
},
mounted() {
this.getBannerData(); // 调用banner接口
}
}
</script>

公共js(filter.js为例)

将公共js部分写在src/filter.js内,在src/main.js中导入即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//filter.js
import Vue from 'vue'
Vue.filter('transformTime', (value)=>{
if(!value) return ''
let date = new Date(value)
let time
if(date.getHours() === 0 ){
time= date.getMinutes() + ":" + date.getSeconds()
}else{
time = date.getHours() +":"+ date.getMinutes() + ":" + date.getSeconds()
}
return time
})

//引用(src/main.js)
import "@/filter.js"

//使用
{{ message | transformTime }}

sass

  • 将公共变量写在 src/assets/css/variables.scss
  • 将公共样式写在 src/assets/css/style.scss
  • 在src/main.js 中引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//variables.scss
$grey:#eee;
$font-title-color:#333;
$font-detail-color:#999;
$font-color:#666;
$red:#c20c0c;
$white:#fff;
$black:#242424;
$blue:#0c73c2;
$body-color:#f5f5f5;
$font-size:14px;
$border-area-color:#d3d3d3;

//style.scss
body {
font-size:$font-size;
color:$font-color;
background-color:$body-color;
}
1
2
3
//main.js
import "@/assets/css/variables.scss"
import "@/assets/css/style.scss"

Comment and share

  • page 1 of 1

John Doe

author.bio


author.job